
package adoc.xml;

import adoc.AdocUtilities;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.jdom.Attribute;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.Namespace;
import org.jdom.input.SAXBuilder;
import org.jdom.xpath.XPath;


/**
 *
 * @author ILikePancakes
 */
public class RelationsDocument extends Document {
    public static final String RELATIONSHIP_MAIN = "http://www.archyvai.lt/adoc/2008/relationships/content/main";
    public static final String RELATIONSHIP_SIGNED = "http://www.archyvai.lt/adoc/2008/relationships/metadata/signable";
    public static final String RELATIONSHIP_UNSIGNED = "http://www.archyvai.lt/adoc/2008/relationships/metadata/unsignable";
    public static final String RELATIONSHIP_APPENDIX = "http://www.archyvai.lt/adoc/2008/relationships/content/appendix";
    public static final String RELATIONSHIP_ATTACHMENT = "http://www.archyvai.lt/adoc/2008/relationships/content/attachment";
    public static final String RELATIONSHIP_THUMBNAIL = "http://www.archyvai.lt/adoc/2008/relationships/thumbnail";
    private Element root;
    private HashMap<String, Element> sourceParts = new HashMap();
    private HashMap<String, Element> relationships = new HashMap();
    private Namespace namespace = Namespace.getNamespace("http://www.archyvai.lt/adoc/2008/relationships");

    public RelationsDocument() {
        super();
        root = new Element("Relationships");
        this.setRootElement(root);
        root.setNamespace(namespace);
        this.setBaseURI("http://www.archyvai.lt/adoc/2008/relationships");
    }

    public RelationsDocument(String mainDocument, String[] appendices, HashMap<String, ArrayList> appendixMap, String[] attachments, String[] metadata, String[] METAINF) {
        this();
        Element sourcePart = addSourcePart("/");

        addRelationship(sourcePart, AdocUtilities.getFileName(mainDocument), RELATIONSHIP_MAIN, null);
        addRelationship(sourcePart, "metadata/signed.xml", RELATIONSHIP_SIGNED, null);
        addRelationship(sourcePart, "metadata/unsigned.xml", RELATIONSHIP_UNSIGNED, null);

        Element main = null;
        if (appendices != null || attachments != null) { main = addSourcePart(AdocUtilities.getFileName((mainDocument))); }
        if (appendices != null) {
            for(int i = 0; i < appendices.length; i++) addRelationship(main, "priedai/" + AdocUtilities.getFileName(appendices[i]), RELATIONSHIP_APPENDIX, null);
            for (String appendixName : appendixMap.keySet()) {
                sourcePart = addSourcePart("priedai/"+AdocUtilities.getFileName(appendixName));
                for (Object aa : appendixMap.get(appendixName)) {
                    addRelationship(sourcePart, "priedai/"+AdocUtilities.getFileName((String) aa), RELATIONSHIP_APPENDIX, null);
                }
            }
        }
        if (attachments != null)
            for(int i = 0; i < attachments.length; i++)
                addRelationship(main, "pridedami/" + AdocUtilities.getFileName(attachments[i]), RELATIONSHIP_ATTACHMENT, null);
    }

    public RelationsDocument(File file) throws IOException, JDOMException {
        super(new SAXBuilder().build(file).cloneContent());
        root = this.getRootElement();
    }

    public RelationsDocument(String filename) throws IOException, JDOMException {
        this(new File(filename));
    }
    
    // metodai gavimui duomenu

    public String getMainDocumentPath() throws JDOMException {
        XPath xpath = XPath.newInstance("/n:Relationships/n:SourcePart[@full-path='/']/n:Relationship[@type='"+RELATIONSHIP_MAIN+"']/@full-path");
        xpath.addNamespace("n", namespace.getURI());
        List<Attribute> paths = xpath.selectNodes(this);
        if (paths != null) {
            if (paths.size() == 1) return paths.get(0).getValue();
            else throw new JDOMException();
        }
        return null;
    }

    public String getFullMainDocumentPath(int UID) throws JDOMException { return AdocUtilities.getWorkingPath(UID) + "/" + getMainDocumentPath(); }

    public List<String> getSignedAttributePaths() {
        try {
            List<Attribute> attributes;
            XPath xpath = XPath.newInstance("/n:Relationships/n:SourcePart[@full-path='/']/n:Relationship[@type='"+RELATIONSHIP_SIGNED+"']/@full-path");
            xpath.addNamespace("n", namespace.getURI());
            attributes = xpath.selectNodes(this);
            ArrayList<String> fullPaths = new ArrayList();
            for (Attribute attribute : attributes) { fullPaths.add(attribute.getValue()); }
            return fullPaths;
        } catch (JDOMException ex) {
            return null;
        }
    }

    public List<String> getFullSignedAttributePaths(int UID) {
        try {
            List<Attribute> attributes;
            XPath xpath = XPath.newInstance("/n:Relationships/n:SourcePart[@full-path='/']/n:Relationship[@type='"+RELATIONSHIP_SIGNED+"']/@full-path");
            xpath.addNamespace("n", namespace.getURI());
            attributes = xpath.selectNodes(this);
            ArrayList<String> fullPaths = new ArrayList();
            for (Attribute attribute : attributes) { fullPaths.add(AdocUtilities.getWorkingPath(UID) + "/" + attribute.getValue()); }
            return fullPaths;
        } catch (JDOMException ex) { return null; }
    }

    public List<String> getUnsignedAttributePaths() {
        try {
            XPath xpath = XPath.newInstance("/n:Relationships/n:SourcePart[@full-path='/']/n:Relationship[@type='"+RELATIONSHIP_UNSIGNED+"']/@full-path");
            xpath.addNamespace("n", namespace.getURI());
            List<Attribute> attributes = xpath.selectNodes(this);
            ArrayList<String> fullPaths = new ArrayList();
            for (Attribute attribute : attributes) {
                fullPaths.add(attribute.getValue());
            }
            return fullPaths;
        } catch (JDOMException ex) { return null; }
    }

    public List<String> getFullUnsignedAttributePaths(int UID) {
        try {
            XPath xpath = XPath.newInstance("/n:Relationships/n:SourcePart[@full-path='/']/n:Relationship[@type='"+RELATIONSHIP_UNSIGNED+"']/@full-path");
            xpath.addNamespace("n", namespace.getURI());
            List<Attribute> attributes = xpath.selectNodes(this);
            ArrayList<String> fullPaths = new ArrayList();
            for (Attribute attribute : attributes) { fullPaths.add(AdocUtilities.getWorkingPath(UID) + "/" + attribute.getValue()); }
            return fullPaths;
        } catch (JDOMException ex) { return null; }
    }

    public List<String> getAppendixPaths() {
        try {
            String mainDocumentPath = this.getMainDocumentPath();
            XPath xpath = XPath.newInstance("/n:Relationships/n:SourcePart[@full-path='" + mainDocumentPath + "']/n:Relationship[@type='"+RELATIONSHIP_APPENDIX+"']/@full-path");
            xpath.addNamespace("n", namespace.getURI());
            List<Attribute> appendices = xpath.selectNodes(this);
            ArrayList<String> appendixPaths = new ArrayList();
            for (Attribute appendix : appendices) {
                appendixPaths.add(appendix.getValue());
            }
            return appendixPaths;
        } catch (JDOMException ex) { return null; }
    }

    public List<String> getAttachmentPaths() {
        try {
            String mainDocumentPath = this.getMainDocumentPath();
            XPath xpath = XPath.newInstance("/n:Relationships/n:SourcePart[@full-path='" + mainDocumentPath + "']/n:Relationship[@type='"+RELATIONSHIP_ATTACHMENT+"']/@full-path");
            xpath.addNamespace("n", namespace.getURI());
            List<Attribute> attachments = xpath.selectNodes(this);
            ArrayList<String> attachmentPaths = new ArrayList();
            for (Attribute attachment : attachments) {
                attachmentPaths.add(attachment.getValue());
            }
            return attachmentPaths;
        } catch (JDOMException jDOMException) { return null; }
    }

    public List<String> getAllAttachmentPaths() {
        try {
            XPath xpath = XPath.newInstance("//n:Relationship[@type='"+RELATIONSHIP_ATTACHMENT+"']/@full-path");
            xpath.addNamespace("n", namespace.getURI());
            List<Attribute> attachments = xpath.selectNodes(this);
            ArrayList<String> attachmentPaths = new ArrayList();
            for (Attribute attachment : attachments) {
                attachmentPaths.add(attachment.getValue());
            }
            return attachmentPaths;
        } catch (JDOMException jDOMException) { return null; }
    }

    public List<String> getAAPaths() {
        try {
            List<String> appendixPaths = this.getAppendixPaths();
            List<Attribute> attributes = new ArrayList();
            XPath xpath = null;
            if (appendixPaths != null)
                for (String path : appendixPaths) {
                    xpath = XPath.newInstance("/n:Relationships/n:SourcePart[@full-path='" + path + "']/n:Relationship[@type='"+RELATIONSHIP_APPENDIX+"']/@full-path");
                    xpath.addNamespace("n", namespace.getURI());
                    attributes.addAll(xpath.selectNodes(this));
                }
            ArrayList<String> paths = new ArrayList();
            for (Attribute attribute : attributes) {
                paths.add(attribute.getValue());
            }
            return paths;
        } catch (JDOMException jDOMException) { return null; }
    }

    public List<String> getAllPaths() {
        try {
            XPath xpath = XPath.newInstance("//n:Relationship[@type='"+RELATIONSHIP_APPENDIX+"' or @type='"+RELATIONSHIP_ATTACHMENT+"' or @type='"+RELATIONSHIP_MAIN+"' or " +
                    "@type='"+RELATIONSHIP_THUMBNAIL+"' or @type='"+RELATIONSHIP_SIGNED+"' or @type='"+RELATIONSHIP_UNSIGNED+"']/@full-path | //n:SourcePart[@full-path='/']/@full-path");
            xpath.addNamespace("n", namespace.getURI());
            List<Attribute> attributes = xpath.selectNodes(this);
            ArrayList<String> paths = new ArrayList();
            for (Attribute attribute : attributes) { paths.add(attribute.getValue()); }
            return paths;
        } catch (JDOMException ex) { return null; }
    }

    public List<String> getAllFullPaths() {
        try {
            XPath xpath = XPath.newInstance("//@full-path");
            xpath.addNamespace("n", namespace.getURI());
            List<Attribute> attributes = xpath.selectNodes(this);
            ArrayList<String> paths = new ArrayList();
            for (Attribute attribute : attributes) { paths.add(attribute.getValue()); }
            return paths;
        } catch (JDOMException ex) { return null; }
    }

    public List<String> getSourcePartFullPaths() {
        try {
            XPath xpath = XPath.newInstance("//n:SourcePart/@full-path");
            xpath.addNamespace("n", namespace.getURI());
            List<Attribute> attributes = xpath.selectNodes(this);
            ArrayList<String> paths = new ArrayList();
            for (Attribute attribute : attributes) { paths.add(attribute.getValue()); }
            return paths;
        } catch (JDOMException ex) { return null; }
    }

    public List<Element> getSourceParts() {
        try {
            XPath xpath = XPath.newInstance("//n:SourcePart");
            xpath.addNamespace("n", namespace.getURI());
            return xpath.selectNodes(this);
        } catch (JDOMException ex) { return null; }
    }

    public Element getRootSourcePart() {
        try {
            XPath xpath = XPath.newInstance("//n:SourcePart[@full-path='/']");
            xpath.addNamespace("n", namespace.getURI());
            return (Element) xpath.selectSingleNode(this);
        } catch (JDOMException ex) { return null; }
    }

    public boolean containsRelationship(Element sourcepart, String fullpath, String type) {
        List<Element> relationships = getRelationshipsBySourcePart(sourcepart);
        if (relationships.contains(this.getRelationshipByPathAndType(fullpath, type))) return true;
        else return false;
    }

    public List<Element> getRelationshipsBySourcePart(Element sourcePart) { return sourcePart.getChildren(); }

    public List<Element> getRelationshipPathsBySourcePart(Element sourcePart) {
        List<Element> relationships = sourcePart.getChildren();
        ArrayList paths = new ArrayList();
        for (Element relationship : relationships) {
            paths.add(relationship.getAttributeValue("full-path"));
        }
        return paths;
    }

    public List<Element> getAllRelationships() {
        try {
            XPath xpath = XPath.newInstance("//n:Relationship");
            xpath.addNamespace("n", namespace.getURI());
            return xpath.selectNodes(this);
        } catch (JDOMException ex) { return null; }
    }

    public String getSourcePartFullPath(Element relationship) {
        for (Object sourcePart : root.getChildren()) {
            Element sp = (Element) sourcePart;
            if (relationship.getParent().equals(sp)) return sp.getAttributeValue("full-path");
        }
        return null;
    }

    public boolean isAppendix(String fullPath) {
        List<String> appendixPaths = this.getAppendixPaths();
        for (String appendixPath : appendixPaths) if (fullPath.equals(appendixPath)) return true;
        return false;
    }
    
    public Element getRelationshipByPath(String fullPath) {
        try {
            XPath xpath = XPath.newInstance("//n:Relationship[@full-path='"+fullPath+"']");
            xpath.addNamespace("n", namespace.getURI());
            return (Element) xpath.selectSingleNode(this);
        } catch (JDOMException ex) { return null; }
    }

    public Element getRelationshipByPathAndType(String fullPath, String type) {
        try {
            XPath xpath = XPath.newInstance("//n:Relationship[@full-path='"+fullPath+"' and @type='"+type+"']");
            xpath.addNamespace("n", namespace.getURI());
            return (Element) xpath.selectSingleNode(this);
        } catch (JDOMException ex) { return null; }
    }

    private boolean referencesAppendix(String fullPath, String originalPath) {
        String parentPath = getSourcePartFullPath(getRelationshipByPath(fullPath));
        if (parentPath.equals(originalPath)) return false;
        if (isAppendix(parentPath)) return true;
        Element rel = getRelationshipByPath(parentPath);
        if (rel != null) {
            return referencesAppendix(rel.getAttributeValue("full-path"), originalPath);
        } else return false;
    }

    public boolean referencesAppendix(String fullPath) {
        return referencesAppendix(fullPath, fullPath);
    }

    public boolean isAttachment(String fullPath) {
        List<String> attachmentPaths = this.getAttachmentPaths();
        for (String attachmentPath : attachmentPaths) if (fullPath.equals(attachmentPath)) return true;
        return false;
    }

    public boolean isMainDocument(String fullPath) {
        try {
            String main = this.getMainDocumentPath();
            if (main.equals(fullPath)) return true;
            else return false;
        } catch (JDOMException ex) { return false; }
    }

    public static boolean isCustomType(String type) {
        if (type.equals(RelationsDocument.RELATIONSHIP_APPENDIX) || type.equals(RelationsDocument.RELATIONSHIP_ATTACHMENT)
                || type.equals(RelationsDocument.RELATIONSHIP_MAIN) || type.equals(RelationsDocument.RELATIONSHIP_THUMBNAIL)
                || type.equals(RelationsDocument.RELATIONSHIP_SIGNED)  || type.equals(RelationsDocument.RELATIONSHIP_UNSIGNED))
            return false;
        else return true;
    }

    // metodai kurimui ir xml formavimui

    public Element addSourcePart(String fullPath) {
        Element sourcePart = new Element("SourcePart", namespace);
        sourcePart.removeAttribute("xmlns");
        sourcePart.setAttribute("full-path", fullPath);
        root.addContent(sourcePart);
        sourceParts.put(fullPath, sourcePart);
        return sourcePart;
    }

    public Element addRelationship(String sourcePartFullPath, String fullPath, String type, String ID) {
        Element sourcePart = sourceParts.get(sourcePartFullPath);
        return addRelationship(sourcePart, fullPath, type, ID);
    }

    public Element addRelationship(Element sourcePart, String fullPath, String type, String ID) {
        Element relationship = new Element("Relationship", namespace);
        sourcePart.addContent(relationship);
        relationship.setAttribute("full-path", fullPath);
        relationship.setAttribute("type", type);
        if (ID != null) relationship.setAttribute("id", ID);
        relationships.put(fullPath, relationship);
        return relationship;
    }

    public Element addElement(String relationshipFullPath, String inSourcePart, String refID) {
        Element relationship = relationships.get(relationshipFullPath);
        return addElement(relationship, inSourcePart, refID);
    }

    public Element addElement(Element relationship, String inSourcePart, String refID) {
        Element element = new Element("Element", namespace);
        relationship.addContent(element);
        element.setAttribute("in-source-part", inSourcePart);
        element.setAttribute("ref-id", refID);
        return element;
    }

}
